#include <windows.h>
#include <dsound.h>
#include <mmsystem.h>
#include <string.h>
#include "logger.h"
#include "pcm-player.h"

const GUID IID_IDirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};
static WAVEFORMATEX waveFormat;
static DSBUFFERDESC bufferDesc;

static LPDIRECTSOUND8 g_pDS = NULL;
static LPDIRECTSOUNDBUFFER g_pDSBuffer = NULL;

#define NUM_NOTIFICATIONS 4

static LPDIRECTSOUNDNOTIFY g_pDSNotify = NULL;
static DSBPOSITIONNOTIFY g_aPosNotify[NUM_NOTIFICATIONS];
static HANDLE g_hNotificationEvent = NULL;
static DWORD g_dwNextWriteOffset = 0;  // 下一个写入位置
static DWORD g_dwBufferSize = 0;

static int check_dsound_support() {
    HRESULT hr;
    LPDIRECTSOUND8 pDS = NULL;
    // 获取 DirectSound 对象
    hr = DirectSoundCreate8(NULL, &pDS, NULL);
    if (FAILED(hr)) {
        elog("DirectSound not supported\n");
        return 0;
    }
    if (pDS) {
        pDS->lpVtbl->Release(pDS);
    }
    return 1;  
}

static int pcmPlayer_init(int channels, int samplerate, int bits_per_sample) {
    HRESULT hr;

    // 获取 DirectSound 对象
    hr = DirectSoundCreate8(NULL, &g_pDS, NULL);
    if (FAILED(hr)) {
        elog("DirectSoundCreate8 failed: %d\n", hr);
        return -1;
    }

    // 设置协作级别
    hr = g_pDS->lpVtbl->SetCooperativeLevel(g_pDS, GetDesktopWindow(), DSSCL_PRIORITY);
    if (FAILED(hr)) {
        elog("SetCooperativeLevel failed: %d\n", hr);
        return -1;
    }

    memset(&waveFormat, 0, sizeof(WAVEFORMATEX));
    waveFormat.wFormatTag = WAVE_FORMAT_PCM;
    waveFormat.nChannels = channels;
    waveFormat.nSamplesPerSec = samplerate;
    waveFormat.wBitsPerSample = bits_per_sample;
    waveFormat.nBlockAlign = (channels * bits_per_sample) / 8;
    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;

    memset(&bufferDesc, 0, sizeof(DSBUFFERDESC));
    bufferDesc.dwSize = sizeof(DSBUFFERDESC);
    bufferDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLVOLUME;
    bufferDesc.dwBufferBytes = waveFormat.nAvgBytesPerSec * 2;
    bufferDesc.lpwfxFormat = &waveFormat;

    // 创建缓冲区
    hr = g_pDS->lpVtbl->CreateSoundBuffer(g_pDS, &bufferDesc, &g_pDSBuffer, NULL);
    if (FAILED(hr)) {
        elog("CreateSoundBuffer failed: %d\n", hr);
        return -1;
    }

    g_dwBufferSize = bufferDesc.dwBufferBytes;

    // 获取 IDirectSoundNotify 接口
    hr = g_pDSBuffer->lpVtbl->QueryInterface(g_pDSBuffer, &IID_IDirectSoundNotify, (LPVOID*)&g_pDSNotify);
    if (FAILED(hr)) {
        elog("QueryInterface for IDirectSoundNotify failed: %d\n", hr);
        return -1;
    }

    // 创建事件
    g_hNotificationEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (g_hNotificationEvent == NULL) {
        elog("CreateEvent failed: %d\n", GetLastError());
        return -1;
    }

    // 设置通知位置
    if(g_dwBufferSize % NUM_NOTIFICATIONS !=0) {
        elog("Buffer size must be multiple of NUM_NOTIFICATIONS\n");
        return -1;
    }
    int  notifySize = g_dwBufferSize / NUM_NOTIFICATIONS;
    for (int i = 0; i < NUM_NOTIFICATIONS; i++) {
        g_aPosNotify[i].dwOffset = notifySize * (i + 1) - 1;
        g_aPosNotify[i].hEventNotify = g_hNotificationEvent;
    }

    hr = g_pDSNotify->lpVtbl->SetNotificationPositions(g_pDSNotify, NUM_NOTIFICATIONS, g_aPosNotify);
    if (FAILED(hr)) {
        elog("SetNotificationPositions failed: %d\n", hr);
        return -1;
    }

    // 开始播放
    hr = g_pDSBuffer->lpVtbl->Play(g_pDSBuffer, 0, 0, 1);
    if (FAILED(hr)) {
        elog("Play failed: %d\n", hr);
        return -1;
    }

    ilog("Windows directSound Audio player initialized\n");

    return 0;
}

static int pcmPlayer_ctrl(player_ctrl_cmd_t cmd, void *arg) {
    if (!g_pDSBuffer) {
        elog("DirectSound buffer not initialized\n");
        return -1;
    }

    HRESULT hr;
    switch (cmd) {
        case PLAYER_CMD_PAUSE:
            hr = g_pDSBuffer->lpVtbl->Stop(g_pDSBuffer);
            if (FAILED(hr)) {
                elog("Stop failed: %d\n", hr);
                return -1;
            }
            break;
        case PLAYER_CMD_RESUME:
            hr = g_pDSBuffer->lpVtbl->Play(g_pDSBuffer, 0, 0, 1);
            if (FAILED(hr)) {
                elog("Play failed: %d\n", hr);
                return -1;
            }
            break;
        case PLAYER_CMD_STOP:
            hr = g_pDSBuffer->lpVtbl->Stop(g_pDSBuffer);
            if (FAILED(hr)) {
                elog("Stop failed: %d\n", hr);
                return -1;
            }
            break;
        default:
            elog("Unknown command\n");
            return -1;
    }

    return 0;
}

static int pcmPlayer_getParams(int *channels, int *samplerate, int *bits_per_sample) {
    if (!channels || !samplerate || !bits_per_sample) {
        return -1;
    }

    *channels = waveFormat.nChannels;
    *samplerate = waveFormat.nSamplesPerSec;
    *bits_per_sample = waveFormat.wBitsPerSample;

    return 0;    
}

static int pcmPlayer_setVolume(int db) {
    if (!g_pDSBuffer) {
        elog("DirectSound buffer not initialized\n");
        return -1;
    }

    LONG lVolume;
    HRESULT hr;
    hr = g_pDSBuffer->lpVtbl->GetVolume(g_pDSBuffer, &lVolume);  // 获取当前音量, 单位是百分百分之一分贝
    if (FAILED(hr)) {
        elog("GetVolume failed: %d\n", hr);
        return -1;
    }
    dlog("Current volume %d\n", lVolume);

    lVolume += db * 100;  // 增益值, 单位是百分之一分贝,所以要乘以100
    if (lVolume > DSBVOLUME_MAX) {
        lVolume = DSBVOLUME_MAX;
    } else if (lVolume < DSBVOLUME_MIN) {
        lVolume = DSBVOLUME_MIN;
    }

    hr = g_pDSBuffer->lpVtbl->SetVolume(g_pDSBuffer, lVolume);
    if (FAILED(hr)) {
        elog("SetVolume failed: %d\n", hr);
        return -1;
    }
    dlog("Volume set to %d\n", lVolume);

    // 返回归一化的音量值(0-100)
    return (lVolume - DSBVOLUME_MIN) * 100 / (DSBVOLUME_MAX - DSBVOLUME_MIN);
}


static int pcmPlayer_write(void *pcm_data, int size) {
    if (!g_pDSBuffer) {
        elog("DirectSound buffer not initialized\n");
        return -1;
    }
    
    if (!pcm_data || size <= 0) {
        elog("Invalid pcm_data or size\n");
        return -1;
    }

    if(size >= g_dwBufferSize / 8 ) {
        elog("size too large\n");
        return -2;
    }

    HRESULT hr;
    void *pDSLockedBuffer = NULL;
    void *pDSLockedBuffer2 = NULL;
    DWORD dwDSLockedBufferSize = 0;
    DWORD dwDSLockedBufferSize2 = 0;

    // 确保我们不会锁定正在播放的部分
    while(1) {
        DWORD dwPlayCursor, dwWriteCursor;
        DWORD dwLockSize = size;
        BOOL  isBufferFull = FALSE;
        hr = g_pDSBuffer->lpVtbl->GetCurrentPosition(g_pDSBuffer, &dwPlayCursor, &dwWriteCursor);
        if (FAILED(hr)) {
            dlog("GetCurrentPosition failed: %d\n", hr);
            return -1;
        }
        dlog("Play Cursor: %lu, writeable Cursor: %lu. Now write pos: %lu, len:%lu\n", dwPlayCursor, dwWriteCursor, g_dwNextWriteOffset, size);
        
        // 简化缓冲区满的检测逻辑
        DWORD dwBytesAvailable;
        if (g_dwNextWriteOffset <= dwPlayCursor) {
            dwBytesAvailable = dwPlayCursor - g_dwNextWriteOffset;
        } else {
            dwBytesAvailable = g_dwBufferSize - g_dwNextWriteOffset + dwPlayCursor;
        }
        
        isBufferFull = (dwBytesAvailable < dwLockSize);
        
        if(!isBufferFull)  break;

        int time1 = timeGetTime();
        DWORD dwResult = WaitForSingleObject(g_hNotificationEvent, 1000);  // 添加1秒超时
        if (dwResult == WAIT_TIMEOUT) {
            elog("Wait for notification event timeout\n");
            return -3;
        }
        int time2 = timeGetTime();
        if(time2 >= time1)
            dlog("Buffer full, wait time: %d ms\n", time2 - time1);
    }

    hr = g_pDSBuffer->lpVtbl->Lock(g_pDSBuffer, g_dwNextWriteOffset, size, &pDSLockedBuffer, &dwDSLockedBufferSize, &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0);
    if (FAILED(hr)) {
        elog("Lock failed: %d\n", hr);
        return -1;
    }

    memcpy(pDSLockedBuffer, (BYTE*)pcm_data, dwDSLockedBufferSize);
    if (pDSLockedBuffer2 != NULL) {
        memcpy(pDSLockedBuffer2, (BYTE*)pcm_data + dwDSLockedBufferSize, dwDSLockedBufferSize2);
    }

    hr = g_pDSBuffer->lpVtbl->Unlock(g_pDSBuffer, pDSLockedBuffer, dwDSLockedBufferSize, pDSLockedBuffer2, dwDSLockedBufferSize2);
    if (FAILED(hr)) {
        elog("Unlock failed: %d\n", hr);
        return -1;
    }

    g_dwNextWriteOffset = (g_dwNextWriteOffset + size) % g_dwBufferSize;

    return 0;
}

static int pcmPlayer_deinit() {
    if(!g_pDSBuffer) {
        elog("DirectSound buffer not initialized\n");
        return -1;
    }
    HRESULT hr;
    DWORD dwStatus;
    int old_status, new_status;
    DWORD dwPlayCursor, dwWriteCursor;
    hr = g_pDSBuffer->lpVtbl->GetCurrentPosition(g_pDSBuffer, &dwPlayCursor, &dwWriteCursor);
    old_status = dwPlayCursor > dwWriteCursor;
    
    // 添加超时机制，最多等待3秒
    DWORD start_time = timeGetTime();
    DWORD timeout_ms = 3000; // 3秒超时
    
    while(1) {
        hr = g_pDSBuffer->lpVtbl->GetStatus(g_pDSBuffer, &dwStatus);
        if (FAILED(hr)) {
            elog("GetStatus failed: %d\n", hr);
            break;
        } 
        if((dwStatus & DSBSTATUS_PLAYING) == 0)  break;  // 播放已经停止
        hr = g_pDSBuffer->lpVtbl->GetCurrentPosition(g_pDSBuffer, &dwPlayCursor, &dwWriteCursor);
        new_status = dwPlayCursor > dwWriteCursor;
        if(old_status != new_status) break;  // 播放位置已经超过写入位置
        
        // 检查是否超时
        if (timeGetTime() - start_time > timeout_ms) {
            elog("Deinit timeout waiting for playback to complete\n");
            break;
        }
        
        Sleep(10); // 减少CPU使用率
    }
    
    // 确保停止播放
    g_pDSBuffer->lpVtbl->Stop(g_pDSBuffer);
    
    if (g_pDSNotify) {
        g_pDSNotify->lpVtbl->Release(g_pDSNotify);
        g_pDSNotify = NULL;
    }

    if (g_pDSBuffer) {
        g_pDSBuffer->lpVtbl->Release(g_pDSBuffer);
        g_pDSBuffer = NULL;
    }

    if (g_pDS) {
        g_pDS->lpVtbl->Release(g_pDS);
        g_pDS = NULL;
    }

    if (g_hNotificationEvent) {
        CloseHandle(g_hNotificationEvent);
        g_hNotificationEvent = NULL;
    }

    dlog("Windows directSound Audio player deinitialized\n");

    return 0;
}

static pcm_player_t pcm_player = {
    .platform = WINNT,
    .name = "DirectSound",
    .check = check_dsound_support,
    .init = pcmPlayer_init,
    .getParams = pcmPlayer_getParams,
    .setVolume = pcmPlayer_setVolume,
    .write = pcmPlayer_write,
    .ctrl = pcmPlayer_ctrl,
    .deinit = pcmPlayer_deinit,
};

__attribute__((constructor))
static void auto_register_pcm_player(void) {
    pcmPlayer_register(&pcm_player);
}